#pragma once

#include <iostream>

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include "socketbase.h"
#include "socket_client_interface.h"
#include "socket_server.h"

#ifndef READ_BUFFER_SIZE
#define READ_BUFFER_SIZE (2048)
#endif

namespace Socket
{
typedef boost::function<void()> callback;

class SocketInterface;
class SocketServer;
class SocketClientInterface;

class SocketSession
{
public:
    SocketSession(boost::shared_ptr<boost::asio::io_service> ios, boost::asio::ip::tcp::resolver::results_type endpoints, 
                  SocketInterface *client) noexcept;

    SocketSession(boost::shared_ptr<boost::asio::io_service> ios, class SocketInterface *server) noexcept;
    SocketSession(boost::shared_ptr<boost::asio::io_service> ios) noexcept;
    ~SocketSession() noexcept;

    bool Start() noexcept;
    void Stop() noexcept;
    void StartConnect(boost::asio::ip::tcp::resolver::results_type::iterator endpoint_iter);
    void HandleConnect(const boost::system::error_code &error, boost::asio::ip::tcp::resolver::results_type::iterator endpoint_iter);

    inline boost::asio::ip::tcp::socket &GetSocket()
    {
        return socket_;
    }

    void StartRead();
    void HandleRead(const boost::system::error_code& error, std::size_t length);

    bool Write(const std::string &str);

    void StartWrite();
    void HandleWrite(const boost::system::error_code& error, std::size_t length);

    std::string get_peer_ip(const boost::asio::ip::tcp::socket &socket) noexcept;
    unsigned short get_peer_port(const boost::asio::ip::tcp::socket &socket) noexcept;
    
private:
    void CheckDeadline();

    boost::shared_ptr<boost::asio::io_service> io_service_;
    boost::asio::ip::tcp::socket socket_;
    boost::asio::steady_timer deadline_;
    boost::asio::steady_timer heartbeat_timer_;
    std::string input_buffer_;
    char buffer_[READ_BUFFER_SIZE];
    boost::asio::ip::tcp::resolver::results_type endpoints_;

    callback read_callback_;
    callback write_callback_;
    callback client_connect_callback_;

    std::string str_read_;
    std::size_t write_length_;
    bool client_is_connect_;

    boost::system::error_code error_code_;

    bool stopped_;
    boost::thread_group executors_thread_group_;
};
};